using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using FolderSync.ShellIntegration;

namespace FolderSync
{
	public partial class FileListView : ListView
	{
		public FileListView()
		{
			InitializeComponent();
			this.ListViewItemSorter = new FileSorter();
		}

		private string rootDirectory;

		[DefaultValue("")]
		public string RootDirectory
		{
			get { return rootDirectory ?? String.Empty; }
			set
			{
				LoadDirectory(value);
				this.rootDirectory = value;
				OnCurrentDirectoryChanged(EventArgs.Empty);
			}
		}

		private List<string> subdirectories = new List<string>();

		private string currentDirectory;

		public string CurrentDirectory
		{
		    get
			{
				if (currentDirectory == null) {
					StringBuilder fullPath = new StringBuilder(RootDirectory);
					foreach (string dir in subdirectories) {
						fullPath.Append(Path.DirectorySeparatorChar);
						fullPath.Append(dir);
					}
					if (fullPath.Length == 0)
						return String.Empty;
					currentDirectory = fullPath.ToString();
				}
				return currentDirectory;
		    }
		}

		public event EventHandler CurrentDirectoryChanged;

		protected virtual void OnCurrentDirectoryChanged(EventArgs e)
		{
			//currentDirectory = null;
			if (CurrentDirectoryChanged != null) {
				CurrentDirectoryChanged(this, e);
			}
		}

		public event ListViewItemSelectionChangedEventHandler DirectoryOpened;

		protected virtual void OnDirectoryOpened(ListViewItemSelectionChangedEventArgs e)
		{
			if (DirectoryOpened != null) {
				DirectoryOpened(this, e);
			}
		}

		public bool IsInSubdirectory
		{
			get { return subdirectories.Count > 0; }
		}

		public void GoUp()
		{
			if (subdirectories.Count > 0) {
				subdirectories.RemoveAt(subdirectories.Count - 1);
				currentDirectory = null;
				LoadDirectory(CurrentDirectory);
				OnCurrentDirectoryChanged(EventArgs.Empty);
			}
		}

		protected override void OnDoubleClick(EventArgs e)
		{
			base.OnDoubleClick(e);
			if (SelectedItems.Count == 1) {
				ListViewItem item = SelectedItems[0];
				if (item.Tag is DirectoryInfo) {
					OpenSubdirectory(item);
				}
			}
		}

		public void OpenSubdirectory(ListViewItem item)
		{
			subdirectories.Add(item.Text);
			currentDirectory = null;
			LoadDirectory(CurrentDirectory);
			OnDirectoryOpened(new ListViewItemSelectionChangedEventArgs(item, item.Index, item.Selected));
			OnCurrentDirectoryChanged(EventArgs.Empty);
		}

		public void RefreshFiles()
		{
			LoadDirectory(CurrentDirectory);
		}

		private void LoadDirectory(string directory)
		{
			if (!String.IsNullOrEmpty(directory)) {
				//MessageBox.Show("Loading... " + this.Name);
				//this.BeginUpdate();
				this.Items.Clear();
				DirectoryInfo dir = new DirectoryInfo(directory);
				if (dir.Exists) {
					foreach (DirectoryInfo sub in dir.GetDirectories()) {
						AddDirectory(sub);
					}
					foreach (FileInfo file in dir.GetFiles()) {
						AddFile(file);
					}
				}
				this.Sort();
				//this.EndUpdate();
			}
		}

		private void AddDirectory(DirectoryInfo directory)
		{
			ListViewItem item = new ListViewItem();
			item.Group = Groups[0];
			item.Text = directory.Name;
			item.ImageKey = AddDirectoryIcon();
			item.Tag = directory;
			this.Items.Add(item);
		}

		private void AddFile(FileInfo file)
		{
			string[] row = new string[] { file.Name, String.Format("{0:n0} KB", Math.Ceiling(file.Length / 1024.0)), file.LastWriteTime.ToString() };
			ListViewItem item = new ListViewItem(row);
			item.Group = Groups[1];
			item.ImageKey = AddFileIcon(file.FullName);
			item.Tag = file;
			this.Items.Add(item);
		}

		private string AddDirectoryIcon()
		{
			const string folder = "folder";
			if (!smallImageList.Images.ContainsKey(folder)) {
				smallImageList.Images.Add(folder, FileIcon.GetFolderIcon(IconSize.Small));
				//largeImageList.Images.Add(folder, FileIcon.GetFolderIcon(IconSize.Large));
			}
			return folder;
		}

		private string AddFileIcon(string path)
		{
			string ext = Path.GetExtension(path);
			if (!String.IsNullOrEmpty(ext)) {
				ext = ext.Substring(1).ToLower();
			}
			if (!smallImageList.Images.ContainsKey(ext)) {
				smallImageList.Images.Add(ext, FileIcon.GetIcon(path, IconSize.Small));
				//largeImageList.Images.Add(ext, FileIcon.GetIcon(path, IconSize.Large));
			}
			return ext;
		}

		#region Sort

		class FileSorter : IComparer
		{
			public int Compare(object x, object y)
			{
				ListViewItem item1 = (ListViewItem)x;
				ListViewItem item2 = (ListViewItem)y;

				if (item1.Tag is DirectoryInfo) {
					if (item2.Tag is DirectoryInfo) {
						return String.Compare(item1.Text, item2.Text, true);
					}
					return -1;
				}
				if (item2.Tag is DirectoryInfo) {
					return 1;
				}
				return String.Compare(item1.Text, item2.Text, true);
			}
		}

		#endregion

		protected override void OnMouseClick(MouseEventArgs e)
		{
			base.OnMouseClick(e);
			if (e.Button == MouseButtons.Right) {
				if (SelectedItems.Count == 1 && SelectedItems[0].Tag is FileInfo) {
					ShellContextMenu scm = new ShellContextMenu();
					FileInfo[] files = new FileInfo[] { (FileInfo)SelectedItems[0].Tag };
					scm.ShowContextMenu(this.Handle, files, Cursor.Position);
				}
			}
		}

		public FileInfo FindFile(string name)
		{
			foreach (ListViewItem item in this.Items) {
				if (item.Tag is FileInfo) {
					if (String.Compare(item.Text, name, true) == 0) {
						return item.Tag as FileInfo;
					}
				}
			}
			return null;
		}

		public ListViewItem FindDirectoryItem(string name)
		{
			foreach (ListViewItem item in this.Items) {
				if (item.Tag is DirectoryInfo) {
					if (String.Compare(item.Text, name, true) == 0) {
						return item;
					}
				}
			}
			return null;
		}

		public DirectoryInfo FindDirectory(string name)
		{
			ListViewItem item = FindDirectoryItem(name);
			if (item != null) {
				return item.Tag as DirectoryInfo;
			}
			return null;
		}

		public enum FileStatus { New, Unchanged, Changed, Deleted }

		public static FileStatus GetStatus(FileInfo left, FileInfo right)
		{
			if (left.Length == right.Length && left.LastWriteTime == right.LastWriteTime) {
				return FileStatus.Unchanged;
			}
			return FileStatus.Changed;
		}

		public static void AssignStatus(FileListView left, FileListView right)
		{
			int statusIndex = left.statusColumnHeader.Index;
			foreach (ListViewItem item in left.Items) {
				FileInfo leftFile = item.Tag as FileInfo;
				if (leftFile != null) {
					FileStatus status = FileStatus.New;
					FileInfo rightFile = right.FindFile(item.Text);
					if (rightFile != null) {
						status = GetStatus(leftFile, rightFile);
					}
					if (statusIndex >= item.SubItems.Count) {
						item.SubItems.Add(new ListViewItem.ListViewSubItem());
					}
					item.SubItems[statusIndex].Text = status.ToString();
				}
			}
		}

		public void CopySelectedFilesTo(string destinationDirectory)
		{
			int count = this.SelectedItems.Count;
			if (count > 0) {
				ShellFileOperation op = new ShellFileOperation();
				List<string> sourceFiles = new List<string>(count);
				List<string> destFiles = new List<string>(count);
				foreach (ListViewItem item in SelectedItems) {
					FileInfo file = item.Tag as FileInfo;
					if (file != null) {
						sourceFiles.Add(file.FullName);
						destFiles.Add(Path.Combine(destinationDirectory, file.Name));
					}
				}
				op.SourceFiles = sourceFiles.ToArray();
				op.DestFiles = destFiles.ToArray();
				op.Operation = ShellFileOperation.FileOperations.FO_COPY;
				op.OwnerWindow = this.Parent.Handle;
				op.DoOperation();
			}
		}
	}
}
